#ifndef SingleList_h
#define SingleList_h

#include "SingleNode.h"


namespace TL {
    template<class T> class SingleList {
    protected:
        SingleNode<T> *m_Head;
        T m_Undefined;
        int m_Size;

    protected:
        SingleNode<T> *get_node(int index)
        {
            SingleNode<T> *targetNode = m_Head;

            for (int i = 1; i <= index; i++) {
                targetNode = targetNode->GetNext();
            }

            return targetNode;
        }
        void zeroing()
        {
            m_Head = 0;
            m_Size = 0;
        }

    public:
        SingleList() : m_Undefined(T())
        {
            zeroing();
        }
        SingleList(const SingleList &list) : m_Undefined(T())
        {
            if (!list.IsEmpty()) {
                SingleNode<T> *left;
                SingleNode<T> *right = list.m_Head;

                m_Head = new SingleNode<T>(right->GetData());

                left = m_Head;
                right = right->GetNext();

                while (right) {
                    left->SetNext(new SingleNode<T>(right->GetData()));

                    left = left->GetNext();
                    right = right->GetNext();
                }
            }
            else {
                m_Head = 0;
            }

            m_Size = list.m_Size;
        }
        ~SingleList()
        {
            Clear();
        }

        T &At(int index)
        {
            if (index < 0 || index >= m_Size) {
                return m_Undefined;
            }

            return get_node(index)->GetData();
        }
        const T &At(int index) const
        {
            if (index < 0 || index >= m_Size) {
                return m_Undefined;
            }

            return get_node(index)->GetData();
        }
        void Clear()
        {
            SingleNode<T> *current_node = m_Head;
            SingleNode<T> *removed_node;

            while (current_node) {
                removed_node = current_node;

                current_node = current_node->GetNext();

                delete removed_node;
            }

            zeroing();
        }
        void Insert(const T &data)
        {
            SingleNode<T> *new_node = new SingleNode<T>(data);

            if (m_Size > 0) {
                SingleNode<T> *last_node = get_node(m_Size - 1);

                last_node->SetNext(new_node);
            }
            else {
                m_Head = new_node;
            }

            m_Size++;
        }
        void Insert(const T &data, int index)
        {
            if (index < 0 || index > m_Size) {
                return;
            }

            SingleNode<T> *new_node = new SingleNode<T>(data);

            if (index > 0) {
                SingleNode<T> *prev_node = get_node(index - 1);

                new_node->SetNext(prev_node->GetNext());
                prev_node->SetNext(new_node);
            }
            else {
                new_node->SetNext(m_Head);

                m_Head = new_node;
            }

            m_Size++;
        }
        bool IsEmpty() const
        {
            return (m_Size == 0);
        }
        void Remove(int index)
        {
            if (index < 0 || index >= m_Size) {
                return;
            }

            SingleNode<T> *removed_node = 0;

            if (index != 0) {
                SingleNode<T> *prev_node = get_node(index - 1);

                removed_node = prev_node->GetNext();

                prev_node->SetNext(removed_node->GetNext());
            }
            else {
                removed_node = m_Head;

                m_Head = removed_node->GetNext();
            }

            if (removed_node) {
                delete removed_node;
            }

            m_Size--;
        }
        int Size() const
        {
            return m_Size;
        }

        SingleList &operator=(const SingleList &list)
        {
            Clear();

            if (!list.IsEmpty()) {
                SingleNode<T> *left;
                SingleNode<T> *right = list.m_Head;

                m_Head = new SingleNode<T>(right->GetData());

                left = m_Head;
                right = right->GetNext();

                while (right) {
                    left->SetNext(new SingleNode<T>(right->GetData()));

                    left = left->GetNext();
                    right = right->GetNext();
                }

                m_Size = list.m_Size;
            }

            return *this;
        }
        T &operator[](int index)
        {
            return get_node(index)->GetData();
        }
        const T &operator[](int index) const
        {
            return get_node(index)->GetData();
        }
    };
}

#endif
